package cn.superchart.module.menu.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.json.JSONUtil;
import cn.superchart.common.base.BaseServiceImpl;
import cn.superchart.common.enums.ExceptionEnum;
import cn.superchart.common.exception.ScException;
import cn.superchart.common.utils.Checker;
import cn.superchart.common.utils.RequestContextUtil;
import cn.superchart.module.menu.entity.ScMenu;
import cn.superchart.module.menu.mapper.ScMenuMapper;
import cn.superchart.module.menu.param.*;
import cn.superchart.module.menu.service.ScMenuService;
import cn.superchart.module.user.entity.ScUser;
import cn.superchart.module.user.service.ScUserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import freemarker.template.Configuration;
import freemarker.template.Template;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
@Service
public class ScMenuServiceImpl extends BaseServiceImpl<ScMenu, ScMenuMapper> implements ScMenuService {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private Configuration configuration;

    @Autowired
    private ScUserService scUserService;


    @Transactional
    @Override
    public void add(ScMenuAddParam scMenuAddParam) {
        ScMenu scMenu = copyProperties(scMenuAddParam,ScMenu.class);
        if(Checker.BeNotNull(scMenuAddParam.getMeta())){
            scMenu.setMeta(JSONUtil.toJsonStr(scMenuAddParam.getMeta()));
        }
        Boolean isExist = exist("route_name",scMenuAddParam.getRouteName(),null);
        if(isExist){
            throw new ScException(ExceptionEnum.HAS_ROW_SAME_FIELD);
        }
        Boolean isPermExist = exist("perms",scMenuAddParam.getPerms(),null);
        if(isPermExist){
            throw new ScException(ExceptionEnum.HAS_ROW_SAME_FLAG);
        }
        super.save(scMenu);
    }


    public Boolean exist(String fieldName,String value,String idVal) {
        if(Checker.BeNotBlank(value)){
            Integer count = baseMapper.exist(fieldName,value,idVal);
            return count>0;
        }
        return false;
    }



    @Override
    public void edit(ScMenuEditParam scMenuEditParam) {
        Boolean isExist = exist("route_name",scMenuEditParam.getRouteName(),scMenuEditParam.getId());
        if(isExist){
            throw new ScException(ExceptionEnum.HAS_ROW_SAME_FIELD);
        }
        Boolean isPermExist = exist("perms",scMenuEditParam.getPerms(),scMenuEditParam.getId());
        if(isPermExist){
            throw new ScException(ExceptionEnum.HAS_ROW_SAME_FLAG);
        }
        ScMenu scMenu = copyProperties(scMenuEditParam,ScMenu.class);
        if(Checker.BeNotNull(scMenuEditParam.getMeta())){
            scMenu.setMeta(JSONUtil.toJsonStr(scMenuEditParam.getMeta()));
        }
        super.updateById(scMenu);
    }

    @Override
    public ScMenuDetailParam detail(String id) {
        ScMenuDetailParam scMenuDetailParam = queryData(id,ScMenuDetailParam.class);
        ScMenu parent = baseMapper.getParent(scMenuDetailParam.getParentId());
        if(Checker.BeNotNull(parent)){
            scMenuDetailParam.setParentId(parent.getId());
            scMenuDetailParam.setParentName(parent.getMenuName());
        }else{
            scMenuDetailParam.setParentId("0");
            scMenuDetailParam.setParentName("顶级");
        }
        return scMenuDetailParam;
    }

    @Override
    public ScMenuData getMenus() {
        ScMenuData scMenuData = new ScMenuData();
        List<ScMenuTree> menus = baseMapper.getMenus(getUserId(),Arrays.asList("0","1"),true);
        //List<String> perms=baseMapper.getMenuPerms(getUserId(),null,true);
        List<Tree<String>> menuTree = buildMenuTree(menus);
        scMenuData.setMenus(menuTree);
        //scMenuData.setPerms(perms);
        return scMenuData;
    }

    @Override
    public void downMenuTs() {
        List<ScMenuTree> menus = baseMapper.getMenus(getUserId(),Arrays.asList("0","1"),true);
        if(Checker.BeNotEmpty(menus)){
            List<Tree<String>> trees = buildMenuTree(menus);
            Map<String,Object> params = new HashMap<>();
            params.put("params",trees);
            File file = FileUtil.createTempFile();
            writeTemp(file,params);
        }
    }

    @Override
    public List<String> getPerms(String userId) {
        List<String> perms=baseMapper.getMenuPerms(userId,null,true);
        return Checker.BeNotEmpty(perms)?perms:Lists.newArrayList();
    }

    @Override
    public List<Tree<String>> menuTrees() {
        List<ScMenuTree> menus=baseMapper.listMenus();
        // return buildMenuTree(menus);
        if(Checker.BeNotEmpty(menus)){
            return TreeUtil.build(toTreeNode(menus),"0",new ScNodeParser<>());
        }
        return buildMenuTree(menus);
    }

    @Override
    public ScMenuTreeData menuTreeForAssign(String userId) {
        ScUser user = scUserService.queryEntity(userId);
        List<ScMenuTree> menus;
        List<String> menuIds = new ArrayList<>();
        if("1".equals(user.getIsFounder())){
             menus = baseMapper.getAllMenus(null,true);
        }else{
             menus = baseMapper.getMenus(getUserId(),null,true);
        }
        if(Checker.BeNotEmpty(menus)){
            menus.forEach(menu->menuIds.add(menu.getId()));
            return new ScMenuTreeData(menuIds,TreeUtil.build(toTreeNode(menus),"0",new ScNodeParser<>())) ;
        }
        return new ScMenuTreeData(menuIds,Lists.newArrayList());
    }

    @Override
    public Map<String, String> urlPerm() {
        List<ScMenu> menus = baseMapper.urlPerm();
        if(Checker.BeNotEmpty(menus)){
            Map<String, String> permMap = new HashMap<>();
            for(ScMenu menu:menus){
                if(Checker.BeNotBlank(menu.getMenuPath()) && Checker.BeNotBlank(menu.getPerms())){
                    String perm = menu.getPerms();
                    String[] paths = menu.getMenuPath().split(";");
                    for(String path:paths){
                        permMap.put(path,perm);
                    }
                }
            }
            return permMap;
        }
        return Maps.newHashMap();
    }


    private void writeTemp(File file,Map<String,Object> params){
       try {
           FileOutputStream outputStream = new FileOutputStream(file);
           Template t = configuration.getTemplate("menu.ts.ftl");
           Writer out = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
           t.process(params, out);
           out.flush();
           out.close();
           outputStream.close();
           down(file);
           log.info("生成成功!");
       }catch (Exception e){
          log.error("生成异常：{}",e.getMessage());
       }
    }

    private void down(File file) throws IOException {
        if(file.exists()){
            HttpServletResponse response = RequestContextUtil.getCurrentResponse();
            response.setCharacterEncoding("utf-8");
            response.setHeader("content-disposition","attachment;fileName="+file.getName());
            response.setHeader("FileName", "menu.ts");
            response.setHeader("Access-Control-Expose-Headers", "FileName");
            try (OutputStream out = response.getOutputStream(); InputStream in = new FileInputStream(file)) {
                byte[] buffer = new byte[1024];
                int i = in.read(buffer);
                while (i != -1) {
                    out.write(buffer, 0, i);//将缓冲区的数据输出到浏览器
                    i = in.read(buffer);
                }
            }finally {
                file.delete();
            }
        }
    }




    private  List<Tree<String>> buildMenuTree( List<ScMenuTree> menus ){
        if(Checker.BeNotEmpty(menus)){
            return TreeUtil.build(toTreeNode(menus),"0");
        }
        return Lists.newArrayList();
    }

    private List<TreeNode<String>> toTreeNode( List<ScMenuTree> menus)  {
        if(Checker.BeNotEmpty(menus)){
            List<TreeNode<String>> nodes = new ArrayList<>();
            for(ScMenuTree menu:menus){
                if(Checker.BeNotBlank(menu.getMetaStr())){
                    menu.setMeta(JSONUtil.toBean(menu.getMetaStr(),Map.class));
                }
                ScTreeNode treeNode = new ScTreeNode();
                treeNode.setId(menu.getId());
                treeNode.setParentId(menu.getParentId());
                treeNode.setWeight(menu.getSortCode());
                treeNode.setExtra(toMap(menu));
                treeNode.setName(menu.getMenuName());
                treeNode.setAttributes(menu);
                nodes.add(treeNode);
            }
            return nodes;
        }
        return Lists.newArrayList();
    }

    private Map toMap(ScMenuTree menu){
        try {
            String jsonString = objectMapper.writeValueAsString(menu);
            Map map = objectMapper.readValue(jsonString, Map.class);
            return map;
        } catch (Exception e) {
             log.error("转化异常：{}",e.getMessage());
        }
        return new HashMap();
    }



}
